#include "VarifyGrpcClient.h"

#include "ConfigMgr.h"

GrpcConPool::GrpcConPool(size_t poolSize, string host, string port)
    : _poolSize(poolSize), _host(host), _port(port), b_stop(false) {
  for (size_t i = 0; i < _poolSize; ++i) {
    std::shared_ptr<Channel> channel = grpc::CreateChannel(
        _host + ":" + _port, grpc::InsecureChannelCredentials());
    _connections.push(VarifyService::NewStub(channel));
  }
}

std::unique_ptr<VarifyService::Stub> GrpcConPool::getConnection() {
  std::unique_lock<std::mutex> lock(_mutex);

  // _cond.wait带‘谓词’的执行流程如下：
  
  /*
     1.线程会立刻执行Lambda函数，若返回true，则继续执行后续代码，不会阻塞线程。
       此时线程仍持有互斥锁 lock，因此可以安全操作共享资源。
       若Lambda返回 false（例如 b_stop 为 false 且 _connections 为空）
       释放互斥锁，避免其他线程无法修改共享资源，并且进入阻塞态，等待唤醒。

     2.唤醒后的处理：
       2.1 重新获取互斥锁，保证后续操作的安全性；
       2.2 再次执行Lambda表达式，检查条件是否满足：
           若Lambda返回 true，继续执行后续代码；
           若仍为 false，再次释放锁并阻塞（循环等待直到条件满足）。

     3.总结
       先执行Lambda检查条件 → 条件满足则继续执行，
       否则释放锁并阻塞 → 唤醒后重新加锁并再次检查条件
  
  */

  _cond.wait(lock, [this] {
    if (b_stop) {
      return true;
    }
    return !_connections.empty();
  });

  // 如果gRpc连接池停止则返回空指针
  if (b_stop) {
    return nullptr;
  }
  auto context = std::move(_connections.front());
  _connections.pop();
  return context;
}

void GrpcConPool::returnConnection(unique_ptr<VarifyService::Stub> context) {
  std::lock_guard<std::mutex> lock(_mutex);
  if (b_stop) {
    return;
  }
  _connections.push(std::move(context));
  _cond.notify_one();
}

void GrpcConPool::close() {
  b_stop = true;
  _cond.notify_all();
}

GrpcConPool::~GrpcConPool() {
  std::lock_guard<std::mutex> lock(_mutex);
  close();
  while (!_connections.empty()) {
    _connections.pop();
  }
}

GetVarifyResponse VarifyGrpcClient::GetVarifyCode(std::string email) {
  ClientContext context;
  GetVarifyResponse response;
  GetVarifyRequest request;
  request.set_email(email);

  auto stub = _gRpcConPool->getConnection();
  Status status = stub->GetVarifyCode(&context, request, &response);
 

  if (status.ok()) {
    _gRpcConPool->returnConnection(std::move(stub));
    return response;
  } else {
    _gRpcConPool->returnConnection(std::move(stub));
    response.set_error_code(ErrorCodes::RPCFailed);
    return response;
  }
}

VarifyGrpcClient::VarifyGrpcClient() {
  auto& cfgMgr = ConfigMgr::GetInstance();
  auto Host = cfgMgr["VarifyServer"]["Host"];
  auto Port = cfgMgr["VarifyServer"]["Port"];
  _gRpcConPool = std::make_unique<GrpcConPool>(2, Host, Port);
}
